#ifndef PRVP_NSShiftPeriod_HPP_
#define PRVP_NSShiftPeriod_HPP_

// Framework includes
#include "../OptFrame/NSSeq.hpp"

// Own includes
#include "ProblemInstance.hpp"
#include "Memory.h"
#include "Solution.h"

using namespace std;

class MoveKShiftPeriod: public Move<RepPRVP, MemPRVP>
{
private:


public:

	using Move<RepPRVP, MemPRVP>::apply; // prevents name hiding
	using Move<RepPRVP, MemPRVP>::canBeApplied; // prevents name hiding

	MoveKShiftPeriod(PRVPProblemInstance& _problemInstance, int _p, int _r, int _c, int _escolha) :
		pPRVP(_problemInstance), periodo(_p), rota(_r), cliente(_c), escolha(_escolha)
	{
	}

	virtual ~MoveKShiftPeriod()
	{
	}

	bool canBeApplied(const MemPRVP& ads, const RepPRVP& rep)
	{
		escolhaReal = pPRVP.dados->at(rep.at(periodo).at(rota).at(cliente)).at((NCOMB + 1) + escolha);
		//cout<<"canbe?!?!?"<<endl;
		//todo fazer estruturas auxiliares que mantes os cargas de cada rota o.O
		//verificando se o cliente cabe nos periodos escolhidos
		int c = rep.at(periodo).at(rota).at(cliente);
		vector<int> pEscolhidos = decompoeEscolha(escolhaReal);

		vector<bool> novaEscolha(pPRVP.nPeriod, false);
		for (int i = 0; i < pEscolhidos.size(); i++)
			novaEscolha[pEscolhidos[i]] = true;

		vector<bool> atualEscolha(pPRVP.nPeriod, false);
		//cout<<"canbe primeiro teste?!?!?"<<endl;
		//vetor que armazena a posição do cliente em cada um dos periodos que ele está presente atualmente
		for(int i =0; i < ads.getLocal_cli().at(c).size();i++)
			atualEscolha.at(ads.getLocal_cli().at(c).at(i)) = true;

		//cout<<"canbe final primeiro teste?!?!?"<<endl;
		if (novaEscolha == atualEscolha)
		{
			invalidos++;
			return false;
		}
		//else
		//	cout<<"canbe, vamos ao segundo teste?!?!?"<<endl;

		//vector<int> pAtual = pPRVP.periodsCli->at(c);

		//vector<double> demandas;
		double demandaP;
		double demanda_cli = pPRVP.dados->at(c).at(DEMANDA);
		//cout<<"canbe - iniciando segundo teste?!?!?"<<endl;
		bool cabe;
		/*
		 cout << endl << "*********************" << endl;
		 cout << "Atual: " << atualEscolha << endl;
		 cout << "nova:" << novaEscolha << endl;*/
		for (int p = 0; p < novaEscolha.size(); ++p)
		{
			//demandas = demandaRota(rep.at(p));
			demandaP = demandaPeriodo(rep.at(p));
			//cout << "Demandas[" << p << "]" << demandas << endl;
			if (novaEscolha[p] && !atualEscolha[p])
			{
				/*cout << "DemandaCli:" << demanda_cli << endl;
				 cout << "CapMax: " << pPRVP.capacidade << endl;*/

				//demandas = demandaRota(rep.at(p));
				cabe = false;

				/*
				 for (int r = 0; r < rep.at(p).size(); ++r)
				 {
				 if (demandas.at(r) + demanda_cli <= pPRVP.capacidade)
				 {
				 demandas.at(r) += demanda_cli;
				 cabe = true;
				 break;
				 }
				 }*/

				if (demandaP + demanda_cli <= pPRVP.nVeic * pPRVP.capacidade)
					cabe = true;

				if (!cabe)
				{
					invalidos++;
					return false;
				}
			}

		}
		validos++;
		//cout<<"canbe - final segundo teste?!?!?"<<endl;
		return true;

	}

	bool canBeApplied(const RepPRVP& rep)
	{
		cout << "ENTROU##################################################" << endl;
		exit(1);
		return true;

	}

	Move<RepPRVP, MemPRVP>& apply(RepPRVP& rep)
	{
		cout << "ENTROU##################################################2" << endl;
		exit(1);
		return *new MoveShiftPeriod(pPRVP, 0, 0, 0, 0);// SEM MOVIMENTO REVERSO
	}

	Move<RepPRVP, MemPRVP>& apply(MemPRVP& ads, RepPRVP& rep)
	{
		int c = rep.at(periodo).at(rota).at(cliente); // id do cliente
		//vector<int> pAtual = pPRVP.periodsCli->at(c); //periodos em que o cliente se encontra atualmente
		vector<int> pEscolhidos = decompoeEscolha(escolhaReal); //periodos que o cliente fará parte após o movimento
		//cout<<"aplicando pertubação"<<endl;
		vector<bool> novaEscolha(pPRVP.nPeriod, false);
		for (int i = 0; i < pEscolhidos.size(); i++)
			novaEscolha[pEscolhidos[i]] = true;

		vector<bool> atualEscolha(pPRVP.nPeriod, false);

		//cout<<"criou vetores"<<endl;
		//vetor que armazena a posição do cliente em cada um dos periodos que ele está presente atualmente

		vector<pair<int, pair<int, int> > > posCli;
		for (unsigned int p = 0; p < pPRVP.nPeriod; p++)
		{
			pair<int, int> rc = findCliInPeriod(c, rep.at(p));
			if (rc.first > -1)
			{
				posCli.push_back(make_pair(p, rc));
				atualEscolha[p] = true;
			}
		}
		/*
		 cout << endl << "@@@@@@@@@@@@@@@@@@@@@@" << endl;
		 cout<<"id do cliente:"<<c<<endl;
		 cout << "Atual: " << atualEscolha << endl;
		 cout << "nova:" << novaEscolha << endl;
		 */
		//cout<<"valor vetores"<<endl;
		//vector<double> demandas;
		//cout << posCli << endl;
		//double demanda_cli = pPRVP.dados->at(c).at(DEMANDA);
		//cout << "removendo..." << endl;
		//removendo todos os clientes iguais ao cliente do movimento
		for (unsigned int p = 0; p < posCli.size(); p++)
		{

			int pp = posCli.at(p).first;
			//demandas = demandaRota(rep.at(pp));
			/*cout << "demandas escolhido:" << demandas << endl;
			 cout << "demanda cli:" << demanda_cli << endl;
			 cout << "capMax:" << pPRVP.capacidade << endl;
			 debug = false;
			 PRVPEvaluator ev(pPRVP);
			 cout<<"Eva:"<<ev.evaluate(rep).evaluation()<<endl;
			 debug = false;*/
			//if (novaEscolha[pp] && !atualEscolha[pp])
			{
				int rr = posCli.at(p).second.first;
				int cc = posCli.at(p).second.second;
				/*cout<<"["<<pp<<","<<rr<<","<<cc<<"]"<<endl;
				 cout<<"remover daki: "<<rep.at(pp).at(rr)<<endl;*/
				//cout<<"rota antes:"<<rep.at(pp).at(rr)<<endl;
				rep.at(pp).at(rr).erase((rep.at(pp).at(rr).begin() + cc));
				//cout<<"rota depois:"<<rep.at(pp).at(rr)<<endl;
			}

		}
		//cout<<"erro?!"<<endl;
		//getchar();
		for (unsigned int p = 0; p < pEscolhidos.size(); p++)
		{
			//cout<<"entrou aki!!"<<endl;
			//if (novaEscolha[pEscolhidos.at(p)] && !atualEscolha[pEscolhidos.at(p)])

			if (!addCliRoutePeriodo(c, rep.at(pEscolhidos.at(p))))
			{
				cout << "############################" << endl;
				cout << "###    FUDEU              ##" << endl;
				cout << "############################" << endl;
				exit(1);
			}
			/*cout<<"saiu daki?!"<<endl;
			 getchar();*/
		}

		//atualizando a memória
		ads.getLocal_cli().at(c) = pEscolhidos;

		//	cout<<"adicionou!!!"<<endl;

		//cout<<"passou?!"<<endl;
		//getchar();
		/*cout << "***ANTES***" << endl;
		 cout << "Escolha: " << pPRVP.escolhas->at(c) << endl;
		 cout << "Periodos: " << pPRVP.periodsCli->at(c) << endl;
		 pPRVP.periodsCli->at(c) = pEscolhidos;
		 pPRVP.escolhas->at(c) = escolhaReal;
		 cout << "***DEPOIS***" << endl;
		 cout<<"=-------------------------------------"<<endl;
		 for (int kk = 0; kk < rep.size(); ++kk)
		 {
		 cout << "p[" << kk << "]:" << rep.at(kk) << endl;
		 }
		 cout<<"=-------------------------------------"<<endl;
		 cout << "Escolha: " << pPRVP.escolhas->at(c) << endl;
		 cout << "Periodos: " << pPRVP.periodsCli->at(c) << endl;
		 //getchar();*/
		return *new MoveShiftPeriod(pPRVP, 0, 0, 0, 0);// SEM MOVIMENTO REVERSO
	}

	virtual bool operator==(const Move<RepPRVP, MemPRVP>& _m) const
	{
		const MoveShiftPeriod& m = (const MoveShiftPeriod&) _m;
		return (periodo == m.periodo) && (rota == m.rota) && (cliente == m.cliente) && (escolha == m.escolha);
	}

	void print() const
	{
		cout << "MoveShiftPeriod( ";
		cout << periodo << ", " << rota << " ," << cliente << ", " << escolha << " )" << endl;
	}
};

class NSShiftPeriod: public NS<RepPRVP, MemPRVP>
{
private:
	PRVPProblemInstance& problemInstance;
public:

	using NS<RepPRVP, MemPRVP>::move; // prevents name hiding

	NSShiftPeriod(PRVPProblemInstance& _problemInstance) :
		problemInstance(_problemInstance)
	{
	}

	virtual ~NSShiftPeriod()
	{
	}

	virtual Move<RepPRVP, MemPRVP>& move(const RepPRVP& rep)
	{
		int period;
		bool one_valid;
		do
		{
			period = rand() % rep.size();
			one_valid = false;
			for (int i = 0; i < rep.at(period).size(); i++)
			{
				if (rep.at(period).at(i).size() > 0)
					one_valid = true;
			}
		} while (! one_valid);

		int rota;
		do
		{
			rota = rand() % rep.at(period).size();
		} while (rep.at(period).at(rota).size() == 0);
		int cliente = rand() % rep.at(period).at(rota).size();
		int escolha = rand() % problemInstance.dados->at(rep.at(period).at(rota).at(cliente)).at(NCOMB);

		return *new MoveShiftPeriod(problemInstance, period, rota, cliente, escolha); // return a random move
	}

	virtual void print()
	{
		cout << "NSShiftPeriod" << endl;
	}
};

#endif /*PRVP_NSShiftPeriod_HPP_*/

